﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Ecopsy.Tools
{
    public class DateTimeTools
    {
        public static readonly DateTime MaxDate = Convert.ToDateTime("01.01.2050");
        public static readonly DateTime MinDate = Convert.ToDateTime("01.01.1950");

        /// <summary>
        /// Разбивка периода сетки загрузки 
        /// </summary>
        public enum TimePeriod { Day, Week, Month };

        public static TimePeriod ConvertToTimePeriod(string val)
        {
            if(TimePeriod.Day.ToString()==val )
                    return TimePeriod.Day;

            if (TimePeriod.Week.ToString() == val)
                return TimePeriod.Week ;
 
            return TimePeriod.Month;
        }

        /// <summary>
        /// возвращает дату - в случае неверного формата или пустой строки - минимальная дата
        /// </summary>
        /// <param name="sDt"></param>
        /// <returns></returns>
        public static DateTime GetDate(string sDt)
        {
            if (string.IsNullOrEmpty(sDt))
                return MinDate;

            try
            {
                return Convert.ToDateTime(sDt);
            }
            catch
            {
                return MinDate;
            }
 
        }

        public static DateTime GetDate(string sDt, DateTime DefaultValue)
        {
            if (string.IsNullOrEmpty(sDt))
                return DefaultValue;

            try
            {
                return Convert.ToDateTime(sDt);
            }
            catch
            {
                return DefaultValue;
            }

        }

        /// <summary>
        /// true если дата вне диапазона 2000-2020 г. или null
        /// </summary>
        /// <param name="Date"></param>
        /// <returns></returns>
        public static bool IsNullOrEmpty(DateTime Date)
        {
            if (null == Date) return true;
            if (Date.Year > 2000 && Date.Year < 2020)
                return false;

            return true;
        }

        public static DateTime ResetTime(DateTime InDate)
        {
            return Convert.ToDateTime(InDate.ToShortDateString());
        }

        public static DateTime AroundDay(DateTime InDate)
        {
            TimeSpan sp = new TimeSpan(23 - InDate.Hour, 60 - InDate.Minute, 60 - InDate.Second, 1000 - InDate.Millisecond);
            return InDate.Add(sp);
        }

        public static DateTime GetFisrtDayInWeek(DateTime now)
        {
            string[] week = { "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday" };
            for (int i = 0; i < 7; i++) 
            {
                    if (week[i].ToLower() == now.DayOfWeek.ToString().ToLower()) 
                        return now.AddDays(i * -1); 
                }
            return now;

        }

        public static int GetDayDiff(DateTime f,DateTime s)
        {   
            TimeSpan ts = f - s;
            return ts.Days;
        }
        public static DateTime GetLastDayInWeek(DateTime now)
        {
            string[] week = { "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday" };
            for (int i = 0; i < 6; i++) 
                { if (week[i].ToLower() == now.DayOfWeek.ToString().ToLower()) 
                        return now.AddDays(6-i); }
            return now;

        }


        public static DateTime GetFisrtDayInMonth(DateTime now)
        {
            return now.AddDays(-1 * now.Day).AddDays(1) ;  

        }

        public static DateTime GetLastDayInMonth(DateTime now)
        {
            int i= DateTime.DaysInMonth(now.Year, now.Month);
            return ((GetFisrtDayInMonth(now)).AddDays(i)).AddDays(-1) ;

        }



        public static bool DayInPeriod(DateTime st, DateTime fn, DateTime Day)
        {
            Day = ResetTime(Day);
            fn = ResetTime(fn);
            st = ResetTime(st);
            return (Day.AddHours(1) > st && Day < fn.AddHours(1));
        }


        /// <summary>
        /// Пполучить разницу в датах в периодах
        /// </summary>
        /// <param name="StartDate"></param>
        /// <param name="EndDate"></param>
        /// <param name="PeriodType"></param>
        /// <returns></returns>
        public static  int GetDiff(DateTime StartDate, DateTime EndDate, TimePeriod PeriodType)
        {
            TimeSpan sp = new TimeSpan ();
            decimal dif =0;

            switch  (PeriodType)
            {
                case TimePeriod .Month :
                    StartDate = GetFisrtDayInMonth(StartDate);
                    EndDate  = GetLastDayInMonth(EndDate);
                    sp= EndDate - StartDate;
                    dif = (sp.Days / 28);
                    return Convert.ToInt16 (  Math .Round (dif));

                case TimePeriod.Week :
                    StartDate = GetFisrtDayInWeek  (StartDate);
                    EndDate = GetLastDayInWeek (EndDate);
                    sp = EndDate - StartDate;
                    dif = (sp.Days / 6);
                    return Convert.ToInt16(Math.Round(dif));
                    return 1;

                
            }
            sp = EndDate - StartDate;
            return sp.Days;
            
        }

        /// <summary>
        /// Вернуть первый день  периода
        /// </summary>
        /// <returns></returns>
        public static DateTime GetFistDayOfPeriod(DateTime StartDate, TimePeriod PeriodType)
        {
            switch (PeriodType)
            { 
                case TimePeriod .Month :
                    return GetFisrtDayInMonth(StartDate) ;                
                case TimePeriod .Week  :
                    return GetFisrtDayInWeek (StartDate);                                
            }
            return StartDate;
        }

        /// <summary>
        /// Вернуть последний день  периода
        /// </summary>
        /// <returns></returns>
        public static DateTime GetLastDayOfPeriod(DateTime StartDate, TimePeriod PeriodType)
        {
            switch (PeriodType)
            {
                case TimePeriod.Month:
                    return GetLastDayInMonth(StartDate);
                case TimePeriod.Week:
                    return GetLastDayInWeek(StartDate);
            }
            return StartDate;
        }


        /// <summary>
        /// Вернуть тип пересечения периодов 
        /// 0 - не пересекаются
        /// 1- X входит в Y 
        /// 2- X левее Y
        /// 3- X правее Y
        /// 4- X==Y
        /// 5-X примыкает к Y 
        /// 6-Y примыкает к X
        /// 7-Y входит в X
        /// </summary>
        /// <param name="xStart"></param>
        /// <param name="xEnd"></param>
        /// <param name="yStart"></param>
        /// <param name="yEnd"></param>
        /// <returns></returns>
        public static int GetCrossPeriod(DateTime xStart, DateTime xEnd, DateTime yStart, DateTime yEnd)
        {
            xStart = ResetTime(xStart);
            xEnd = ResetTime(xEnd);
            yStart = ResetTime(yStart);
            yEnd = ResetTime(yEnd);

            int GrossType = 0;
            
            //не пересекаются
            if ((!DayInPeriod(yStart, yEnd, xStart) && !DayInPeriod(yStart, yEnd, xEnd)))
            {
                // 5-X примыкает к Y справа
                if (yEnd.Subtract(xStart).Days == -1)
                    return 5;
                // 6-X примыкает к Y слева
                if (yStart.Subtract(xEnd).Days == 1)
                    return 6;
            }
            else
            {

                // идентичны
                if (xStart.Equals(xStart) && xEnd.Equals(yEnd))
                    return 4;

                //X входит в Y 
                if (DayInPeriod(yStart, yEnd, xStart) && DayInPeriod(yStart, yEnd, xEnd))
                    return 1;

                //Y входит в X 
                if (DayInPeriod(xStart, xEnd, yStart) && DayInPeriod(xStart, xEnd, yEnd))
                    return 7;

                // 2- X левее Y
                if (DayInPeriod(yStart, yEnd, xEnd))
                    return 2;
                // 3- X правее Y
                if (DayInPeriod(yStart, yEnd, xStart))
                    return 3;                

            }

            return GrossType;
        }

        /// <summary>
        /// Проверка периода - проверить формат даты и правило - дат начала < даты окончания 
        /// </summary>
        /// <param name="sDtStart"></param>
        /// <param name="sDtFinish"></param>
        /// <param name="DtStrat"></param>
        /// <param name="DtFinish"></param>
        /// <returns></returns>
        public static string CheckPeriod(string sDtStart, string sDtFinish, ref DateTime DtStrat, ref  DateTime DtFinish)
        {

            if (!DateTime.TryParse(sDtStart, out DtStrat))
                return "Неверный формат даты ";

            if (!DateTime.TryParse(sDtFinish, out DtFinish))
                return "Неверный формат даты ";

            if (DtStrat > DtFinish)
                return "Дата начала больше даты окончания периода";

            return string.Empty;

        }

         //<summary>
         //вернуть дату в неразрывном формате SQL ГодМесяцДень
         //</summary>
         //<param name="Day"></param>
         //<returns></returns>
        public static string GetSqlDate (DateTime Day)
        {
            return Day.ToString("yyyMMdd");
        }

        public static string GetTxtDate(DateTime Day)        
        {
            if (Day > MaxDate || Day < MinDate)
                return string.Empty;
            return Day.ToShortDateString();
        }


    }
}
